package com.cloudbroker.bcs.common.configure;

import java.text.MessageFormat;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.cloudbroker.bcs.common.util.CollectionUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cloudbroker.bcs.common.configure.constants.ConfigureConstants;
import com.cloudbroker.bcs.common.configure.model.TenantInfo;
import com.cloudbroker.bcs.common.util.ValueUtil;

public class TenantConfig {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(TenantConfig.class);
    
    private String tenantId;
    
    private Map<String, Object> allInOneConfig = new ConcurrentHashMap<String, Object>();
    
    private Map<String, Map<String, Object>> categorySpecConfigMap = new ConcurrentHashMap<String, Map<String, Object>>();
    
    private Lock lock = new ReentrantLock();
    
    public TenantConfig(String tenantId) {
        this.tenantId = tenantId;
    }
    
    public TenantInfo getTenantInfo() {
        String tenantName = ValueUtil.getString(allInOneConfig.get(ConfigureConstants.CONFIG_KEY_TENANT_NAME));
        return new TenantInfo(tenantId, tenantName);
    }
    
    private Object getValueInternal(String key) {
        Map<String, Object> prop = allInOneConfig;
        if (prop.containsKey(key)) {
            return prop.get(key);
        }
        return null;
    }
    
    public String getString(String key, String defaultValue) {
        Object value = getValueInternal(key);
        return null == value ? defaultValue : String.valueOf(value);
    }
    
    public String getString(String key) {
        return getString(key, "");
    }
    
    public int getInt(String key, int defaultValue) {
        Object value = getValueInternal(key);
        if (null == value) {
            return defaultValue;
        }
        if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        try {
            return Integer.parseInt(String.valueOf(value));
        } catch (Exception e) {
            LOGGER.error("getInt error", e);
            return defaultValue;
        }
    }
    
    public long getLong(String key, long defaultValue) {
        Object value = getValueInternal(key);
        if (null == value) {
            return defaultValue;
        }
        if (value instanceof Number) {
            return ((Number) value).longValue();
        }
        try {
            return Long.parseLong(String.valueOf(value));
        } catch (Exception e) {
            LOGGER.error("getLong error", e);
            return defaultValue;
        }
    }
    
    public boolean getBoolean(String key, boolean defaultValue) {
        Object value = getValueInternal(key);
        if (null == value) {
            return defaultValue;
        }
        return Boolean.parseBoolean(String.valueOf(value));
    }
    
    public Double getDouble(String key, double defaultValue) {
        Object value = getValueInternal(key);
        if (null == value) {
            return defaultValue;
        }
        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }
        try {
            return Double.parseDouble(String.valueOf(value));
        } catch (Exception e) {
            LOGGER.error("getDouble error", e);
            return defaultValue;
        }
    }
    
    public String get(String key) {
        return getString(key, "");
    }
    
    public String get(String key, String defaultValue) {
        return getString(key, defaultValue);
    }
    
    public String getWithFormat(String key, Object... vars) {
        String format = getString(key, null);
        if (StringUtils.isNotBlank(format)) {
            MessageFormat mf = new MessageFormat(format);
            return mf.format(vars);
        }
        return "";
    }
    
    public Integer get(String key, Integer defaultValue) {
        Object value = getValueInternal(key);
        if (null == value) {
            return defaultValue;
        }
        if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        try {
            return Integer.valueOf(String.valueOf(value));
        } catch (Exception e) {
            LOGGER.error("getInteger error", e);
            return defaultValue;
        }
    }
    
    public Long get(String key, Long defaultValue) {
        Object value = getValueInternal(key);
        if (null == value) {
            return defaultValue;
        }
        if (value instanceof Number) {
            return ((Number) value).longValue();
        }
        try {
            return Long.valueOf(String.valueOf(value));
        } catch (Exception e) {
            LOGGER.error("getLong error", e);
            return defaultValue;
        }
    }
    
    public Boolean get(String key, Boolean defaultValue) {
        Object value = getValueInternal(key);
        if (null == value) {
            return defaultValue;
        }
        return Boolean.valueOf(String.valueOf(value));
    }
    
    public Double get(String key, Double defaultValue) {
        Object value = getValueInternal(key);
        if (null == value) {
            return defaultValue;
        }
        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }
        try {
            return Double.valueOf(String.valueOf(value));
        } catch (Exception e) {
            LOGGER.error("getDouble error", e);
            return defaultValue;
        }
    }
    
    public Map<String, Object> getAllInOneConfig() {
        return allInOneConfig;
    }
    
    public Map<String, Object> getCategorySpecConfig(String category) {
        return categorySpecConfigMap.get(category);
    }
    
    public void updateConfig(String category, Map<String, Object> configuration) {
        lock.lock();
        try {
            System.out.println( category + " ===============> " + configuration);
            categorySpecConfigMap.put(category, configuration);
            refreshAllInOneConfig();
        } finally {
            lock.unlock();
        }
    }
    
    private void refreshAllInOneConfig() {
        allInOneConfig.clear();
        for (Map<String, Object> configs : categorySpecConfigMap.values()) {
            allInOneConfig.putAll(configs);
        }
    }
    
    public void clear() {
        lock.lock();
        try {
            allInOneConfig.clear();
            categorySpecConfigMap.clear();
        } finally {
            lock.unlock();
        }
    }
    
    public String getTenantId() {
        return tenantId;
    }
    
    @Override
    public String toString() {
        return "TenantConfig [tenantId=" + tenantId + ", categorySpecConfigMap=" + categorySpecConfigMap + "]";
    }

    public void putTenantConfig(TenantConfig config) {
        CollectionUtil.merge(allInOneConfig , config.getAllInOneConfig() , false);
    }
}
